package org.quickjsf.core.base.controller.support;

import java.util.Date;
import java.util.List;
import java.util.ResourceBundle;

import javax.faces.component.UIComponent;
import javax.faces.component.UIInput;
import javax.faces.component.UISelectItem;
import javax.faces.component.UISelectItems;
import javax.faces.component.html.HtmlInputText;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.event.ActionEvent;

import org.apache.commons.lang3.StringUtils;
import org.omnifaces.converter.SelectItemsConverter;
import org.primefaces.component.calendar.Calendar;
import org.primefaces.component.inputtext.InputText;
import org.primefaces.component.outputpanel.OutputPanel;
import org.primefaces.component.selectonemenu.SelectOneMenu;
import org.primefaces.context.RequestContext;
import org.quickjsf.common.util.DomainUtil;
import org.quickjsf.common.util.FacesUtil;
import org.quickjsf.core.base.controller.BaseDTPanelController;
import org.quickjsf.core.base.controller.IFilterable;
import org.quickjsf.core.base.viewmodel.support.FilterableViewModelSupport;
import org.quickjsf.core.base.viewmodel.vo.ColumnModel;
import org.quickjsf.core.base.viewmodel.vo.FieldFilter;
import org.quickjsf.core.base.viewmodel.vo.FieldFilter.CondType;
import org.quickjsf.core.base.viewmodel.vo.FieldFilter.CondTypeName;
import org.quickjsf.core.datamodel.OptionType;

public abstract class FilterableDTPanelSupport<T> extends AbsDTPanelSupport<T> {
	
	private FilterableViewModelSupport<T> filterViewModel;
	
	public static final String FILTER_VALUE_PANEL_ID = "filter_value_panel";
	public static final String FIELD_SELECT_ITEMS_ID = "field_select_items";
	public static final String CONDS_SELECT_ID = "conds_select";
	
	
	public FilterableDTPanelSupport(FilterableViewModelSupport<T> filterViewModel) {
		this.filterViewModel = filterViewModel;
	}
	
	public boolean addFilterComponent(OutputPanel filterValuePanel, String field) {
		return false;
	}
	
	private final boolean autoAddFilterComponent(OutputPanel filterValuePanel, String field) {
		if (getBaseDTPanelController().getOptionTypes() != null 
				&& getBaseDTPanelController().getOptionTypes().get(field) != null) {
			return filterValuePanel.getChildren().add(createFieldOptionTypesComponent(getColumnModelByField(field)));
		}
		return false;
	}
	
	public void updateComponentOnPanel(/*IFilterable<T> callback*/) {
		OutputPanel filterValuePanel = (OutputPanel) FacesUtil
		.getUIComponent(getBaseDTPanelController().getFullListFormId() + FacesUtil.getSeparatorChar() + FILTER_VALUE_PANEL_ID);
		//remove all origin children components
		filterValuePanel.getChildren().clear();
		
		//清除验证错误后的状态
		RequestContext.getCurrentInstance().reset(
				getBaseDTPanelController().getFullListFormId() + FacesUtil.getSeparatorChar() + CONDS_SELECT_ID);
		
		if (autoAddFilterComponent(filterValuePanel, filterViewModel.getFieldFilter().getField())) {
			filterViewModel.setCondLabel(CondTypeName.EQ);
			filterViewModel.getFieldFilter().setCondition(CondType.EQ);
			return;
		}
		if (filterViewModel.getFieldFilter().getField() == null || 
				filterViewModel.getFieldFilter().getField().toUpperCase().equals("ALL")) {
			InputText inputText = createInputTextComponent(getFieldFilterValueEL(), null, null);
			//设置LABEL
			inputText.setLabel(ResourceBundle.getBundle("messages.configures").getString("query.field"));
			filterValuePanel.getChildren().add(inputText);
			
			filterViewModel.setCondLabel(null);
			filterViewModel.getFieldFilter().setCondition(null);
		} else {
			Class<?> returnType = getFilterFieldReturnType(filterViewModel.getFieldFilter().getField());
			ColumnModel columnModel = getColumnModelByField(filterViewModel.getFieldFilter().getField());
			Converter converter = getColumnConverter(columnModel, returnType);
			
			if (returnType.equals(Date.class)) {
				filterValuePanel.getChildren().add(createCalendarComponent(getFieldFilterValueEL(), columnModel, converter));
				filterValuePanel.getChildren().add(createCalendarComponent(getFieldFilterOtherValueEL(), columnModel, converter));
				
				filterViewModel.setCondLabel(CondTypeName.BETWEEN);
				filterViewModel.getFieldFilter().setCondition(CondType.BETWEEN);
			} else if (returnType.equals(Boolean.class)) {
				filterValuePanel.getChildren().add(createSelectOneMenuComponent(
						getFieldFilterValueEL(), "#{"+BaseDTPanelController.QUICKJSF_CONTAINER_ID+".booleanOptionMap}", columnModel));
				
				filterViewModel.setCondLabel(CondTypeName.EQ);
				filterViewModel.getFieldFilter().setCondition(CondType.EQ);
			} else {
				filterValuePanel.getChildren().add(createInputTextComponent(getFieldFilterValueEL(), columnModel, converter));
				
				if (returnType.equals(String.class)) {
					filterViewModel.setCondLabel(CondTypeName.LIKE);
					filterViewModel.getFieldFilter().setCondition(CondType.LIKE);
				} else {
					filterViewModel.setCondLabel(CondTypeName.EQ);
					filterViewModel.getFieldFilter().setCondition(CondType.EQ);
				}
			}
		}
	}
	
	/**
	 * 设置查询输入组件的Converter
	 * @param input
	 * @param filed
	 * @param returnType
	 */
	private Converter getColumnConverter(ColumnModel columnModel, Class<?> returnType) {
		if (columnModel.getConverter() != null) {
			return columnModel.getConverter();
		}
		//设置默认Converter
		if (null != returnType) {
			return FacesUtil.getFacesConverterByType(returnType);
		}
		
		return null;
	}
	
	private ColumnModel getColumnModelByField(String filed) {
		for (ColumnModel columnModel : getBaseDTPanelController().getColumns()) {
			if (filed.equals(columnModel.getProperty())) {
				return columnModel;
			}
		}
		return null;
	}
	
	public final String getFieldFilterValueEL() {
		return "#{"+getBaseDTPanelController().getManageBeanName()+".viewModel.filterViewModel.fieldFilter.value}";
	}
	
	public final String getFieldFilterOtherValueEL() {
		return "#{"+getBaseDTPanelController().getManageBeanName()+".viewModel.filterViewModel.fieldFilter.otherValue}";
	}
	
	private Class<?> getFilterFieldReturnType(String field) {
		Class<?> returnType = null;
		try {
			if (!StringUtils.isEmpty(filterViewModel.getFieldFilter().getValueCls())) {
				returnType = Class.forName(filterViewModel.getFieldFilter().getValueCls());
			}
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		if (returnType == null) {
			returnType = DomainUtil.getFieldReturnType(filterViewModel.getDTPanelViewModel().getEntityCls(), field);
		}
		return returnType;
	}
	
	@SuppressWarnings("unchecked")
	public void showOtherComponentOnPanel() throws Exception {
		OutputPanel filterValuePanel = (OutputPanel) FacesUtil
			.getUIComponent(getBaseDTPanelController().getFullListFormId() + FacesUtil.getSeparatorChar() + FILTER_VALUE_PANEL_ID);
		
		//设置CondLabel
		filterViewModel.setCondLabel(FieldFilter.getConditionName(filterViewModel.getFieldFilter().getCondition()));
		
		if (!filterViewModel.getFieldFilter().getCondition().equals(CondType.BETWEEN)) {
			if (filterValuePanel.getChildren().size() == 2) {
				filterValuePanel.getChildren().remove(1);
			}
		} else {
			UIComponent inputValueComponent = filterValuePanel.getChildren().get(0);
			filterValuePanel.getChildren().clear();
			UIComponent otherInputValueComponent = inputValueComponent.getClass().newInstance();
			DomainUtil.copyProperty(otherInputValueComponent, "styleClass", inputValueComponent);
			DomainUtil.copyProperty(otherInputValueComponent, "converter", inputValueComponent);
			
			//设置Required
			if (otherInputValueComponent instanceof UIInput) {
				((UIInput)otherInputValueComponent).setRequired(true);
			}
			//设置Label
			if (otherInputValueComponent instanceof HtmlInputText) {
				((HtmlInputText)otherInputValueComponent).setLabel(((HtmlInputText)inputValueComponent).getLabel());
			}
			
			//下拉框
			if (otherInputValueComponent instanceof SelectOneMenu) {
				List<UIComponent> children = (List<UIComponent>) DomainUtil.invoke(inputValueComponent, "getChildren", null);
				//多个SelectItem形式
				if (children.size() > 0 && children.get(0) instanceof UISelectItem) {
					for (UIComponent component : children) {
						UISelectItem otherSelectItem = (UISelectItem) new UISelectItem();
						otherSelectItem.setItemLabel(((UISelectItem)component).getItemLabel());
						otherSelectItem.setItemValue(((UISelectItem)component).getItemValue());
						otherInputValueComponent.getChildren().add(otherSelectItem);
					}
				} 
				//单个SelectItems形式
				else if (children.size() == 1 && children.get(0) instanceof UISelectItems) {
					UISelectItems selectItems = (UISelectItems) children.get(0);
					UISelectItems otherSelectItems = selectItems.getClass().newInstance();
					otherSelectItems.setValue(selectItems.getValue());
					otherInputValueComponent.getChildren().add(otherSelectItems);
				}
				
				//Calendar日历类型
			} else if (otherInputValueComponent instanceof Calendar) {
				Calendar calendar = (Calendar)otherInputValueComponent;
				calendar.setShowButtonPanel(true);
				calendar.setNavigator(true);
				calendar.setLocale(((Calendar)inputValueComponent).getLocale());
				calendar.setPattern((((Calendar)inputValueComponent).getPattern()));
			}
			
			otherInputValueComponent.setValueExpression("value", FacesUtil.createValueExpression(getFieldFilterOtherValueEL()));
			filterValuePanel.getChildren().add(inputValueComponent);
			filterValuePanel.getChildren().add(otherInputValueComponent);
		}
	}
	
	public final InputText createInputTextComponent(String valueEl, ColumnModel columnModel, Converter converter) {
		InputText inputText = new InputText();
		inputText.setRequired(true);
		inputText.setConverter(converter);
		if (columnModel != null) {
			inputText.setLabel(columnModel.getHeader());
		}
		inputText.setStyleClass("menubar_options_elet filter_value_cls");
		inputText.setValueExpression("value", FacesUtil.createValueExpression(valueEl));
		
		return inputText;
	}
	
	public final SelectOneMenu createSelectOneMenuComponent(String valueEl, String itemsValueEl, ColumnModel columnModel) {
		SelectOneMenu selectOneMenu = new SelectOneMenu();
		selectOneMenu.setRequired(true);
		if (columnModel != null) {
			selectOneMenu.setLabel(columnModel.getHeader());
		}
		selectOneMenu.setValueExpression("value", FacesUtil.createValueExpression(valueEl));
		selectOneMenu.setStyleClass("fields_select_cls filter_value_cls");
		selectOneMenu.setConverter(new SelectItemsConverter());
		UISelectItems selectItems = new UISelectItems();
		selectItems.setValueExpression("value", FacesUtil.createValueExpression(itemsValueEl));
		selectOneMenu.getChildren().add(selectItems);
		
		return selectOneMenu;
	}
	
	public final SelectOneMenu createFieldOptionTypesComponent(ColumnModel columnModel) {
		SelectOneMenu selectOneMenu = new SelectOneMenu();
		selectOneMenu.setRequired(true);
		selectOneMenu.setValueExpression("value", FacesUtil.createValueExpression(getFieldFilterValueEL()));
		selectOneMenu.setStyleClass("fields_select_cls filter_value_cls");
		selectOneMenu.setConverter(new SelectItemsConverter());
		if (columnModel != null) {
			selectOneMenu.setLabel(columnModel.getHeader());
		}
		
		for (OptionType optionType : getBaseDTPanelController().getOptionTypes().get(columnModel.getProperty())) {
			UISelectItem selectItem = new UISelectItem();
			selectItem.setItemLabel(optionType.getName());
			selectItem.setItemValue(optionType.getValue());
			selectOneMenu.getChildren().add(selectItem);
		}
		
		return selectOneMenu;
	}
	
	public Calendar createCalendarComponent(String valueEl, ColumnModel columnModel, Converter converter) {
		Calendar calendar = new Calendar();
		calendar.setConverter(converter);
		calendar.setRequired(true);
		if (columnModel != null) {
			calendar.setLabel(columnModel.getHeader());
		}
		calendar.setLocale(FacesUtil.getLocale());
		calendar.setShowButtonPanel(true);
		calendar.setNavigator(true);
		Object pattern = DomainUtil.getFieldValue(converter, "pattern");
		if (pattern != null) {
			calendar.setPattern(pattern.toString());
		}
		calendar.setStyleClass("menubar_options_elet filter_value_cls");
		calendar.setValueExpression("value", FacesUtil.createValueExpression(valueEl));
		
		return calendar;
	}
	
	public final void searchFilter(IFilterable<T> callback) {
		// before filter 
		callback.beforeFilter();
		// filter implement 
		getBaseDTPanelController().resetDataTable();
		if (filterViewModel.getFieldFilter().getField().toUpperCase().equals("ALL")) {
			searchAll();
			filterViewModel.getFieldFilter().setConvertValue(
					filterViewModel.getFieldFilter().getValue().toString()+"&");
		} else if (StringUtils.isEmpty(filterViewModel.getFieldFilter().getConvertValue())) {
			setConvertValue();
		}
		
		filterViewModel.getDTPanelViewModel().getDataModel().getFieldFilters().add(filterViewModel.getFieldFilter());
		filterViewModel.setFieldFilter(new FieldFilter());
		updateComponentOnPanel();
		filterViewModel.setCondLabel(null);
		
		// after filter
		callback.afterFilter();
	}

	private void setConvertValue() {
		//先转换value对象的值，再设置到convertValue中
		for (ColumnModel columnModel : getBaseDTPanelController().getColumns()) {
			if (columnModel.getConverter() != null 
					&& filterViewModel.getFieldFilter().getField().equals(columnModel.getProperty())) {
				
				OutputPanel filterValuePanel = (OutputPanel) FacesUtil
					.getUIComponent(getBaseDTPanelController().getFullListFormId() + FacesUtil.getSeparatorChar() + FILTER_VALUE_PANEL_ID);
				
				//value
				String val = columnModel.getConverter().getAsString(FacesContext
						.getCurrentInstance(), filterValuePanel.getChildren().get(0), filterViewModel.getFieldFilter().getValue());
				//otherValue
				String otherVal = null;
				if (filterValuePanel.getChildren().size() > 1 
						&& filterViewModel.getFieldFilter().getOtherValue() != null) {
					otherVal = columnModel.getConverter().getAsString(FacesContext
						.getCurrentInstance(), filterValuePanel.getChildren().get(1), filterViewModel.getFieldFilter().getOtherValue());
				}
				filterViewModel.getFieldFilter().setConvertValue(val+"&"+otherVal);
				break;
			} 
		}
		//直接把value对象设置到convertValue中
		if (StringUtils.isEmpty(filterViewModel.getFieldFilter().getConvertValue())) {
			filterViewModel.getFieldFilter().setConvertValue(filterViewModel.getFieldFilter().getValue() 
					+ "&" + filterViewModel.getFieldFilter().getOtherValue());
		}
	}
	
	public final void removeFilterBookmark(ActionEvent event) {
		int index = Integer.valueOf(FacesUtil.getRequestParameter("filterIndex"));
		filterViewModel.getDTPanelViewModel().getDataModel().getFieldFilters().remove(index);
	}
	
	@SuppressWarnings("unchecked")
	private void searchAll() {
		List<ColumnModel> fieldItems = (List<ColumnModel>) ((UISelectItems) 
				FacesUtil.getUIComponent(getBaseDTPanelController().getFullListFormId() + FacesUtil.getSeparatorChar() + FIELD_SELECT_ITEMS_ID)).getValue();
		String[] fields = new String[fieldItems.size()];
		String[] valClses = new String[fieldItems.size()];
		int index = 0;
		for (ColumnModel columnModel : fieldItems) {
			fields[index] = columnModel.getProperty();
			valClses[index] = getFilterFieldReturnType(columnModel.getProperty()).getName();
			index++;
		}
		filterViewModel.getFieldFilter().setValueCls(StringUtils.join(valClses, FieldFilter.FIELD_SPLIT));
		filterViewModel.getFieldFilter().setCondition(CondType.ALL_COND);
		filterViewModel.getFieldFilter().setField(StringUtils.join(fields, FieldFilter.FIELD_SPLIT));
	}
}
